/*
 * ProGuard -- shrinking, optimization, obfuscation, and preverification of Java bytecode.
 *
 * Copyright (c) 2002-2018 GuardSquare NV
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this program; if not, write to the Free
 * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */
package proguard;

import java.util.List;

import proguard.classfile.ClassConstants;
import proguard.io.DataEntryNameFilter;
import proguard.io.DataEntryReader;
import proguard.io.FilteredDataEntryReader;
import proguard.io.JarReader;
import proguard.io.PrefixStrippingDataEntryReader;
import proguard.util.ExtensionMatcher;
import proguard.util.FileNameParser;
import proguard.util.ListParser;

/**
 * This class can create DataEntryReader instances based on class path entries. The readers will unwrap the input data
 * entries from any jars, wars, ears, jmods, and zips before passing them to a given reader.
 *
 * @author Eric Lafortune
 */
public class DataEntryReaderFactory {
    /**
     * Creates a DataEntryReader that can read the given class path entry.
     *
     * @param messagePrefix a prefix for messages that are printed out.
     * @param classPathEntry the input class path entry.
     * @param reader a data entry reader to which the reading of actual classes and resource files can be delegated.
     * @return a DataEntryReader for reading the given class path entry.
     */
    public static DataEntryReader createDataEntryReader(String messagePrefix, ClassPathEntry classPathEntry,
        DataEntryReader reader) {
        boolean isApk = classPathEntry.isApk();
        boolean isJar = classPathEntry.isJar();
        boolean isAar = classPathEntry.isAar();
        boolean isWar = classPathEntry.isWar();
        boolean isEar = classPathEntry.isEar();
        boolean isJmod = classPathEntry.isJmod();
        boolean isZip = classPathEntry.isZip();

        List filter = classPathEntry.getFilter();
        List apkFilter = classPathEntry.getApkFilter();
        List jarFilter = classPathEntry.getJarFilter();
        List aarFilter = classPathEntry.getAarFilter();
        List warFilter = classPathEntry.getWarFilter();
        List earFilter = classPathEntry.getEarFilter();
        List jmodFilter = classPathEntry.getJmodFilter();
        List zipFilter = classPathEntry.getZipFilter();

        System.out
            .println(
                messagePrefix
                    + (isApk ? "apk"
                        : isJar ? "jar"
                            : isAar ? "aar"
                                : isWar ? "war" : isEar ? "ear" : isJmod ? "jmod" : isZip ? "zip" : "directory")
                    + " [" + classPathEntry.getName() + "]"
                    + (filter != null || apkFilter != null || jarFilter != null || aarFilter != null
                        || warFilter != null || earFilter != null || jmodFilter != null || zipFilter != null
                            ? " (filtered)" : ""));

        // Add a filter, if specified.
        if (filter != null) {
            reader = new FilteredDataEntryReader(
                new DataEntryNameFilter(new ListParser(new FileNameParser()).parse(filter)), reader);
        }

        // Unzip any apks, if necessary.
        reader = wrapInJarReader(reader, false, false, isApk, apkFilter, ".apk");
        if (!isApk) {
            // Unzip any jars, if necessary.
            reader = wrapInJarReader(reader, false, false, isJar, jarFilter, ".jar");
            if (!isJar) {
                // Unzip any aars, if necessary.
                reader = wrapInJarReader(reader, false, false, isAar, aarFilter, ".aar");
                if (!isAar) {
                    // Unzip any wars, if necessary.
                    reader = wrapInJarReader(reader, true, false, isWar, warFilter, ".war");
                    if (!isWar) {
                        // Unzip any ears, if necessary.
                        reader = wrapInJarReader(reader, false, false, isEar, earFilter, ".ear");
                        if (!isEar) {
                            // Unzip any jmods, if necessary.
                            reader = wrapInJarReader(reader, true, true, isJmod, jmodFilter, ".jmod");
                            if (!isJmod) {
                                // Unzip any zips, if necessary.
                                reader = wrapInJarReader(reader, false, false, isZip, zipFilter, ".zip");
                            }
                        }
                    }
                }
            }
        }

        return reader;
    }

    /**
     * Wraps the given DataEntryReader in a JarReader, filtering it if necessary.
     * 
     * @param reader the data entry reader that can read the entries contained in the jar file.
     * @param stripClassesPrefix specifies whether to strip the ""classes/" prefix from contained .class data entries.
     * @param stripJmodHeader specifies whether to strip the jmod magic bytes from the zip.
     * @param isJar specifies whether the data entries should always be unzipped.
     * @param jarFilter otherwise, an optional filter on the data entry names.
     * @param jarExtension also otherwise, a required data entry name extension.
     * @return a DataEntryReader for reading the entries of jar file data entries.
     */
    private static DataEntryReader wrapInJarReader(DataEntryReader reader, boolean stripClassesPrefix,
        boolean stripJmodHeader, boolean isJar, List jarFilter, String jarExtension) {
        if (stripClassesPrefix) {
            reader = new FilteredDataEntryReader(
                new DataEntryNameFilter(new ExtensionMatcher(ClassConstants.CLASS_FILE_EXTENSION)),
                new PrefixStrippingDataEntryReader(ClassConstants.JMOD_CLASS_FILE_PREFIX, reader), reader);
        }

        // Unzip any jars, if necessary.
        DataEntryReader jarReader = new JarReader(reader, stripJmodHeader);

        if (isJar) {
            // Always unzip.
            return jarReader;
        } else {
            // Add a filter, if specified.
            if (jarFilter != null) {
                jarReader = new FilteredDataEntryReader(
                    new DataEntryNameFilter(new ListParser(new FileNameParser()).parse(jarFilter)), jarReader);
            }

            // Only unzip the right type of jars.
            return new FilteredDataEntryReader(new DataEntryNameFilter(new ExtensionMatcher(jarExtension)), jarReader,
                reader);
        }
    }
}
